//-----------------------------------------------------------------------------
// Copyright (C) 2017 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// APDU status bytes information
//-----------------------------------------------------------------------------

#include "apduinfo.h"

#include <stdio.h>
#include <string.h>
#include "ui.h"


const APDUCode APDUCodeTable[] = {
	//  ID             Type                  Description
	{"XXXX",    APDUCODE_TYPE_NONE,         ""}, // blank string
	{"6---",    APDUCODE_TYPE_ERROR,        "Class not supported."},
	{"61--",    APDUCODE_TYPE_INFO,         "Response bytes still available"},
	{"61XX",    APDUCODE_TYPE_INFO,         "Command successfully executed; 'XX' bytes of data are available and can be requested using GET RESPONSE."},
	{"62--",    APDUCODE_TYPE_WARNING,      "State of non-volatile memory unchanged"},
	{"6200",    APDUCODE_TYPE_WARNING,      "No information given (NV-Ram not changed)"},
	{"6201",    APDUCODE_TYPE_WARNING,      "NV-Ram not changed 1."},
	{"6281",    APDUCODE_TYPE_WARNING,      "Part of returned data may be corrupted"},
	{"6282",    APDUCODE_TYPE_WARNING,      "End of file/record reached before reading Le bytes"},
	{"6283",    APDUCODE_TYPE_WARNING,      "Selected file invalidated"},
	{"6284",    APDUCODE_TYPE_WARNING,      "Selected file is not valid. FCI not formated according to ISO"},
	{"6285",    APDUCODE_TYPE_WARNING,      "No input data available from a sensor on the card. No Purse Engine enslaved for R3bc"},
	{"62A2",    APDUCODE_TYPE_WARNING,      "Wrong R-MAC"},
	{"62A4",    APDUCODE_TYPE_WARNING,      "Card locked (during reset( ))"},
	{"62CX",    APDUCODE_TYPE_WARNING,      "Counter with value x (command dependent)"},
	{"62F1",    APDUCODE_TYPE_WARNING,      "Wrong C-MAC"},
	{"62F3",    APDUCODE_TYPE_WARNING,      "Internal reset"},
	{"62F5",    APDUCODE_TYPE_WARNING,      "Default agent locked"},
	{"62F7",    APDUCODE_TYPE_WARNING,      "Cardholder locked"},
	{"62F8",    APDUCODE_TYPE_WARNING,      "Basement is current agent"},
	{"62F9",    APDUCODE_TYPE_WARNING,      "CALC Key Set not unblocked"},
	{"62FX",    APDUCODE_TYPE_WARNING,      "-"},
	{"62XX",    APDUCODE_TYPE_WARNING,      "RFU"},
	{"63--",    APDUCODE_TYPE_WARNING,      "State of non-volatile memory changed"},
	{"6300",    APDUCODE_TYPE_WARNING,      "No information given (NV-Ram changed)"},
	{"6381",    APDUCODE_TYPE_WARNING,      "File filled up by the last write. Loading/updating is not allowed."},
	{"6382",    APDUCODE_TYPE_WARNING,      "Card key not supported."},
	{"6383",    APDUCODE_TYPE_WARNING,      "Reader key not supported."},
	{"6384",    APDUCODE_TYPE_WARNING,      "Plaintext transmission not supported."},
	{"6385",    APDUCODE_TYPE_WARNING,      "Secured transmission not supported."},
	{"6386",    APDUCODE_TYPE_WARNING,      "Volatile memory is not available."},
	{"6387",    APDUCODE_TYPE_WARNING,      "Non-volatile memory is not available."},
	{"6388",    APDUCODE_TYPE_WARNING,      "Key number not valid."},
	{"6389",    APDUCODE_TYPE_WARNING,      "Key length is not correct."},
	{"63C0",    APDUCODE_TYPE_WARNING,      "Verify fail, no try left."},
	{"63C1",    APDUCODE_TYPE_WARNING,      "Verify fail, 1 try left."},
	{"63C2",    APDUCODE_TYPE_WARNING,      "Verify fail, 2 tries left."},
	{"63C3",    APDUCODE_TYPE_WARNING,      "Verify fail, 3 tries left."},
	{"63CX",    APDUCODE_TYPE_WARNING,      "The counter has reached the value 'x' (0 = x = 15) (command dependent)."},
	{"63F1",    APDUCODE_TYPE_WARNING,      "More data expected."},
	{"63F2",    APDUCODE_TYPE_WARNING,      "More data expected and proactive command pending."},
	{"63FX",    APDUCODE_TYPE_WARNING,      "-"},
	{"63XX",    APDUCODE_TYPE_WARNING,      "RFU"},
	{"64--",    APDUCODE_TYPE_ERROR,        "State of non-volatile memory unchanged"},
	{"6400",    APDUCODE_TYPE_ERROR,        "No information given (NV-Ram not changed)"},
	{"6401",    APDUCODE_TYPE_ERROR,        "Command timeout. Immediate response required by the card."},
	{"64XX",    APDUCODE_TYPE_ERROR,        "RFU"},
	{"65--",    APDUCODE_TYPE_ERROR,        "State of non-volatile memory changed"},
	{"6500",    APDUCODE_TYPE_ERROR,        "No information given"},
	{"6501",    APDUCODE_TYPE_ERROR,        "Write error. Memory failure. There have been problems in writing or reading the EEPROM. Other hardware problems may also bring this error."},
	{"6581",    APDUCODE_TYPE_ERROR,        "Memory failure"},
	{"65FX",    APDUCODE_TYPE_ERROR,        "-"},
	{"65XX",    APDUCODE_TYPE_ERROR,        "RFU"},
	{"66--",    APDUCODE_TYPE_SECURITY,     " "},
	{"6600",    APDUCODE_TYPE_SECURITY,     "Error while receiving (timeout)"},
	{"6601",    APDUCODE_TYPE_SECURITY,     "Error while receiving (character parity error)"},
	{"6602",    APDUCODE_TYPE_SECURITY,     "Wrong checksum"},
	{"6603",    APDUCODE_TYPE_SECURITY,     "The current DF file without FCI"},
	{"6604",    APDUCODE_TYPE_SECURITY,     "No SF or KF under the current DF"},
	{"6669",    APDUCODE_TYPE_SECURITY,     "Incorrect Encryption/Decryption Padding"},
	{"66XX",    APDUCODE_TYPE_SECURITY,     "-"},
	{"67--",    APDUCODE_TYPE_ERROR,        " "},
	{"6700",    APDUCODE_TYPE_ERROR,        "Wrong length"},
	{"67XX",    APDUCODE_TYPE_ERROR,        "length incorrect (procedure)(ISO 7816-3)"},
	{"68--",    APDUCODE_TYPE_ERROR,        "Functions in CLA not supported"},
	{"6800",    APDUCODE_TYPE_ERROR,        "No information given (The request function is not supported by the card)"},
	{"6881",    APDUCODE_TYPE_ERROR,        "Logical channel not supported"},
	{"6882",    APDUCODE_TYPE_ERROR,        "Secure messaging not supported"},
	{"6883",    APDUCODE_TYPE_ERROR,        "Last command of the chain expected"},
	{"6884",    APDUCODE_TYPE_ERROR,        "Command chaining not supported"},
	{"68FX",    APDUCODE_TYPE_ERROR,        "-"},
	{"68XX",    APDUCODE_TYPE_ERROR,        "RFU"},
	{"69--",    APDUCODE_TYPE_ERROR,        "Command not allowed"},
	{"6900",    APDUCODE_TYPE_ERROR,        "No information given (Command not allowed)"},
	{"6901",    APDUCODE_TYPE_ERROR,        "Command not accepted (inactive state)"},
	{"6981",    APDUCODE_TYPE_ERROR,        "Command incompatible with file structure"},
	{"6982",    APDUCODE_TYPE_ERROR,        "Security condition not satisfied."},
	{"6983",    APDUCODE_TYPE_ERROR,        "Authentication method blocked"},
	{"6984",    APDUCODE_TYPE_ERROR,        "Referenced data reversibly blocked (invalidated)"},
	{"6985",    APDUCODE_TYPE_ERROR,        "Conditions of use not satisfied."},
	{"6986",    APDUCODE_TYPE_ERROR,        "Command not allowed (no current EF)"},
	{"6987",    APDUCODE_TYPE_ERROR,        "Expected secure messaging (SM) object missing"},
	{"6988",    APDUCODE_TYPE_ERROR,        "Incorrect secure messaging (SM) data object"},
	{"698D",    APDUCODE_TYPE_NONE,         "Reserved"},
	{"6996",    APDUCODE_TYPE_ERROR,        "Data must be updated again"},
	{"69E1",    APDUCODE_TYPE_ERROR,        "POL1 of the currently Enabled Profile prevents this action."},
	{"69F0",    APDUCODE_TYPE_ERROR,        "Permission Denied"},
	{"69F1",    APDUCODE_TYPE_ERROR,        "Permission Denied - Missing Privilege"},
	{"69FX",    APDUCODE_TYPE_ERROR,        "-"},
	{"69XX",    APDUCODE_TYPE_ERROR,        "RFU"},
	{"6A--",    APDUCODE_TYPE_ERROR,        "Wrong parameter(s) P1-P2"},
	{"6A00",    APDUCODE_TYPE_ERROR,        "No information given (Bytes P1 and/or P2 are incorrect)"},
	{"6A80",    APDUCODE_TYPE_ERROR,        "The parameters in the data field are incorrect."},
	{"6A81",    APDUCODE_TYPE_ERROR,        "Function not supported"},
	{"6A82",    APDUCODE_TYPE_ERROR,        "File not found"},
	{"6A83",    APDUCODE_TYPE_ERROR,        "Record not found"},
	{"6A84",    APDUCODE_TYPE_ERROR,        "There is insufficient memory space in record or file"},
	{"6A85",    APDUCODE_TYPE_ERROR,        "Lc inconsistent with TLV structure"},
	{"6A86",    APDUCODE_TYPE_ERROR,        "Incorrect P1 or P2 parameter."},
	{"6A87",    APDUCODE_TYPE_ERROR,        "Lc inconsistent with P1-P2"},
	{"6A88",    APDUCODE_TYPE_ERROR,        "Referenced data not found"},
	{"6A89",    APDUCODE_TYPE_ERROR,        "File already exists"},
	{"6A8A",    APDUCODE_TYPE_ERROR,        "DF name already exists."},
	{"6AF0",    APDUCODE_TYPE_ERROR,        "Wrong parameter value"},
	{"6AFX",    APDUCODE_TYPE_ERROR,        "-"},
	{"6AXX",    APDUCODE_TYPE_ERROR,        "RFU"},
	{"6B--",    APDUCODE_TYPE_ERROR,        " "},
	{"6B00",    APDUCODE_TYPE_ERROR,        "Wrong parameter(s) P1-P2"},
	{"6BXX",    APDUCODE_TYPE_ERROR,        "Reference incorrect (procedure byte), (ISO 7816-3)"},
	{"6C--",    APDUCODE_TYPE_ERROR,        "Wrong length Le"},
	{"6C00",    APDUCODE_TYPE_ERROR,        "Incorrect P3 length."},
	{"6CXX",    APDUCODE_TYPE_ERROR,        "Bad length value in Le; 'xx' is the correct exact Le"},
	{"6D--",    APDUCODE_TYPE_ERROR,        " "},
	{"6D00",    APDUCODE_TYPE_ERROR,        "Instruction code not supported or invalid"},
	{"6DXX",    APDUCODE_TYPE_ERROR,        "Instruction code not programmed or invalid (procedure byte), (ISO 7816-3)"},
	{"6E--",    APDUCODE_TYPE_ERROR,        " "},
	{"6E00",    APDUCODE_TYPE_ERROR,        "Class not supported"},
	{"6EXX",    APDUCODE_TYPE_ERROR,        "Instruction class not supported (procedure byte), (ISO 7816-3)"},
	{"6F--",    APDUCODE_TYPE_ERROR,        "Internal exception"},
	{"6F00",    APDUCODE_TYPE_ERROR,        "Command aborted - more exact diagnosis not possible (e.g., operating system error)."},
	{"6FFF",    APDUCODE_TYPE_ERROR,        "Card dead (overuse)"},
	{"6FXX",    APDUCODE_TYPE_ERROR,        "No precise diagnosis (procedure byte), (ISO 7816-3)"},
	{"9---",    APDUCODE_TYPE_NONE,         ""},
	{"9000",    APDUCODE_TYPE_INFO,         "Command successfully executed (OK)."},
	{"9004",    APDUCODE_TYPE_WARNING,      "PIN not successfully verified, 3 or more PIN tries left"},
	{"9008",    APDUCODE_TYPE_NONE,         "Key/file not found"},
	{"9080",    APDUCODE_TYPE_WARNING,      "Unblock Try Counter has reached zero"},
	{"9100",    APDUCODE_TYPE_NONE,         "OK"},
	{"9101",    APDUCODE_TYPE_NONE,         "States.activity, States.lock Status or States.lockable has wrong value"},
	{"9102",    APDUCODE_TYPE_NONE,         "Transaction number reached its limit"},
	{"910C",    APDUCODE_TYPE_NONE,         "No changes"},
	{"910E",    APDUCODE_TYPE_NONE,         "Insufficient NV-Memory to complete command"},
	{"911C",    APDUCODE_TYPE_NONE,         "Command code not supported"},
	{"911E",    APDUCODE_TYPE_NONE,         "CRC or MAC does not match data"},
	{"9140",    APDUCODE_TYPE_NONE,         "Invalid key number specified"},
	{"917E",    APDUCODE_TYPE_NONE,         "Length of command string invalid"},
	{"919D",    APDUCODE_TYPE_NONE,         "Not allow the requested command"},
	{"919E",    APDUCODE_TYPE_NONE,         "Value of the parameter invalid"},
	{"91A0",    APDUCODE_TYPE_NONE,         "Requested AID not present on PICC"},
	{"91A1",    APDUCODE_TYPE_NONE,         "Unrecoverable error within application"},
	{"91AE",    APDUCODE_TYPE_NONE,         "Authentication status does not allow the requested command"},
	{"91AF",    APDUCODE_TYPE_NONE,         "Additional data frame is expected to be sent"},
	{"91BE",    APDUCODE_TYPE_NONE,         "Out of boundary"},
	{"91C1",    APDUCODE_TYPE_NONE,         "Unrecoverable error within PICC"},
	{"91CA",    APDUCODE_TYPE_NONE,         "Previous Command was not fully completed"},
	{"91CD",    APDUCODE_TYPE_NONE,         "PICC was disabled by an unrecoverable error"},
	{"91CE",    APDUCODE_TYPE_NONE,         "Number of Applications limited to 28"},
	{"91DE",    APDUCODE_TYPE_NONE,         "File or application already exists"},
	{"91EE",    APDUCODE_TYPE_NONE,         "Could not complete NV-write operation due to loss of power"},
	{"91F0",    APDUCODE_TYPE_NONE,         "Specified file number does not exist"},
	{"91F1",    APDUCODE_TYPE_NONE,         "Unrecoverable error within file"},
	{"920x",    APDUCODE_TYPE_INFO,         "Writing to EEPROM successful after 'x' attempts."},
	{"9210",    APDUCODE_TYPE_ERROR,        "Insufficient memory. No more storage available."},
	{"9240",    APDUCODE_TYPE_ERROR,        "Writing to EEPROM not successful."},
	{"9301",    APDUCODE_TYPE_NONE,         "Integrity error"},
	{"9302",    APDUCODE_TYPE_NONE,         "Candidate S2 invalid"},
	{"9303",    APDUCODE_TYPE_ERROR,        "Application is permanently locked"},
	{"9400",    APDUCODE_TYPE_ERROR,        "No EF selected."},
	{"9401",    APDUCODE_TYPE_NONE,         "Candidate currency code does not match purse currency"},
	{"9402",    APDUCODE_TYPE_NONE,         "Candidate amount too high"},
	{"9402",    APDUCODE_TYPE_ERROR,        "Address range exceeded."},
	{"9403",    APDUCODE_TYPE_NONE,         "Candidate amount too low"},
	{"9404",    APDUCODE_TYPE_ERROR,        "FID not found, record not found or comparison pattern not found."},
	{"9405",    APDUCODE_TYPE_NONE,         "Problems in the data field"},
	{"9406",    APDUCODE_TYPE_ERROR,        "Required MAC unavailable"},
	{"9407",    APDUCODE_TYPE_NONE,         "Bad currency : purse engine has no slot with R3bc currency"},
	{"9408",    APDUCODE_TYPE_NONE,         "R3bc currency not supported in purse engine"},
	{"9408",    APDUCODE_TYPE_ERROR,        "Selected file type does not match command."},
	{"9580",    APDUCODE_TYPE_NONE,         "Bad sequence"},
	{"9681",    APDUCODE_TYPE_NONE,         "Slave not found"},
	{"9700",    APDUCODE_TYPE_NONE,         "PIN blocked and Unblock Try Counter is 1 or 2"},
	{"9702",    APDUCODE_TYPE_NONE,         "Main keys are blocked"},
	{"9704",    APDUCODE_TYPE_NONE,         "PIN not successfully verified, 3 or more PIN tries left"},
	{"9784",    APDUCODE_TYPE_NONE,         "Base key"},
	{"9785",    APDUCODE_TYPE_NONE,         "Limit exceeded - C-MAC key"},
	{"9786",    APDUCODE_TYPE_NONE,         "SM error - Limit exceeded - R-MAC key"},
	{"9787",    APDUCODE_TYPE_NONE,         "Limit exceeded - sequence counter"},
	{"9788",    APDUCODE_TYPE_NONE,         "Limit exceeded - R-MAC length"},
	{"9789",    APDUCODE_TYPE_NONE,         "Service not available"},
	{"9802",    APDUCODE_TYPE_ERROR,        "No PIN defined."},
	{"9804",    APDUCODE_TYPE_ERROR,        "Access conditions not satisfied, authentication failed."},
	{"9835",    APDUCODE_TYPE_ERROR,        "ASK RANDOM or GIVE RANDOM not executed."},
	{"9840",    APDUCODE_TYPE_ERROR,        "PIN verification not successful."},
	{"9850",    APDUCODE_TYPE_ERROR,        "INCREASE or DECREASE could not be executed because a limit has been reached."},
	{"9862",    APDUCODE_TYPE_ERROR,        "Authentication Error, application specific (incorrect MAC)"},
	{"9900",    APDUCODE_TYPE_NONE,         "1 PIN try left"},
	{"9904",    APDUCODE_TYPE_NONE,         "PIN not successfully verified, 1 PIN try left"},
	{"9985",    APDUCODE_TYPE_NONE,         "Wrong status - Cardholder lock"},
	{"9986",    APDUCODE_TYPE_ERROR,        "Missing privilege"},
	{"9987",    APDUCODE_TYPE_NONE,         "PIN is not installed"},
	{"9988",    APDUCODE_TYPE_NONE,         "Wrong status - R-MAC state"},
	{"9A00",    APDUCODE_TYPE_NONE,         "2 PIN try left"},
	{"9A04",    APDUCODE_TYPE_NONE,         "PIN not successfully verified, 2 PIN try left"},
	{"9A71",    APDUCODE_TYPE_NONE,         "Wrong parameter value - Double agent AID"},
	{"9A72",    APDUCODE_TYPE_NONE,         "Wrong parameter value - Double agent Type"},
	{"9D05",    APDUCODE_TYPE_ERROR,        "Incorrect certificate type"},
	{"9D07",    APDUCODE_TYPE_ERROR,        "Incorrect session data size"},
	{"9D08",    APDUCODE_TYPE_ERROR,        "Incorrect DIR file record size"},
	{"9D09",    APDUCODE_TYPE_ERROR,        "Incorrect FCI record size"},
	{"9D0A",    APDUCODE_TYPE_ERROR,        "Incorrect code size"},
	{"9D10",    APDUCODE_TYPE_ERROR,        "Insufficient memory to load application"},
	{"9D11",    APDUCODE_TYPE_ERROR,        "Invalid AID"},
	{"9D12",    APDUCODE_TYPE_ERROR,        "Duplicate AID"},
	{"9D13",    APDUCODE_TYPE_ERROR,        "Application previously loaded"},
	{"9D14",    APDUCODE_TYPE_ERROR,        "Application history list full"},
	{"9D15",    APDUCODE_TYPE_ERROR,        "Application not open"},
	{"9D17",    APDUCODE_TYPE_ERROR,        "Invalid offset"},
	{"9D18",    APDUCODE_TYPE_ERROR,        "Application already loaded"},
	{"9D19",    APDUCODE_TYPE_ERROR,        "Invalid certificate"},
	{"9D1A",    APDUCODE_TYPE_ERROR,        "Invalid signature"},
	{"9D1B",    APDUCODE_TYPE_ERROR,        "Invalid KTU"},
	{"9D1D",    APDUCODE_TYPE_ERROR,        "MSM controls not set"},
	{"9D1E",    APDUCODE_TYPE_ERROR,        "Application signature does not exist"},
	{"9D1F",    APDUCODE_TYPE_ERROR,        "KTU does not exist"},
	{"9D20",    APDUCODE_TYPE_ERROR,        "Application not loaded"},
	{"9D21",    APDUCODE_TYPE_ERROR,        "Invalid Open command data length"},
	{"9D30",    APDUCODE_TYPE_ERROR,        "Check data parameter is incorrect (invalid start address)"},
	{"9D31",    APDUCODE_TYPE_ERROR,        "Check data parameter is incorrect (invalid length)"},
	{"9D32",    APDUCODE_TYPE_ERROR,        "Check data parameter is incorrect (illegal memory check area)"},
	{"9D40",    APDUCODE_TYPE_ERROR,        "Invalid MSM Controls ciphertext"},
	{"9D41",    APDUCODE_TYPE_ERROR,        "MSM controls already set"},
	{"9D42",    APDUCODE_TYPE_ERROR,        "Set MSM Controls data length less than 2 bytes"},
	{"9D43",    APDUCODE_TYPE_ERROR,        "Invalid MSM Controls data length"},
	{"9D44",    APDUCODE_TYPE_ERROR,        "Excess MSM Controls ciphertext"},
	{"9D45",    APDUCODE_TYPE_ERROR,        "Verification of MSM Controls data failed"},
	{"9D50",    APDUCODE_TYPE_ERROR,        "Invalid MCD Issuer production ID"},
	{"9D51",    APDUCODE_TYPE_ERROR,        "Invalid MCD Issuer ID"},
	{"9D52",    APDUCODE_TYPE_ERROR,        "Invalid set MSM controls data date"},
	{"9D53",    APDUCODE_TYPE_ERROR,        "Invalid MCD number"},
	{"9D54",    APDUCODE_TYPE_ERROR,        "Reserved field error"},
	{"9D55",    APDUCODE_TYPE_ERROR,        "Reserved field error"},
	{"9D56",    APDUCODE_TYPE_ERROR,        "Reserved field error"},
	{"9D57",    APDUCODE_TYPE_ERROR,        "Reserved field error"},
	{"9D60",    APDUCODE_TYPE_ERROR,        "MAC verification failed"},
	{"9D61",    APDUCODE_TYPE_ERROR,        "Maximum number of unblocks reached"},
	{"9D62",    APDUCODE_TYPE_ERROR,        "Card was not blocked"},
	{"9D63",    APDUCODE_TYPE_ERROR,        "Crypto functions not available"},
	{"9D64",    APDUCODE_TYPE_ERROR,        "No application loaded"},
	{"9E00",    APDUCODE_TYPE_NONE,         "PIN not installed"},
	{"9E04",    APDUCODE_TYPE_NONE,         "PIN not successfully verified, PIN not installed"},
	{"9F00",    APDUCODE_TYPE_NONE,         "PIN blocked and Unblock Try Counter is 3"},
	{"9F04",    APDUCODE_TYPE_NONE,         "PIN not successfully verified, PIN blocked and Unblock Try Counter is 3"},
	{"9FXX",    APDUCODE_TYPE_NONE,         "Command successfully executed; 'xx' bytes of data are available and can be requested using GET RESPONSE."},
	{"9XXX",    APDUCODE_TYPE_NONE,         "Application related status, (ISO 7816-3)"}
};
const size_t APDUCodeTableLen = sizeof(APDUCodeTable) / sizeof(APDUCode);

static int CodeCmp(const char *code1, const char *code2) {
	int xsymb = 0;
	int cmp = 0;
	for (int i = 0; i < 4; i++) {
		if (code1[i] == code2[i])
			cmp++;
		if (code1[i] == 'X' || code2[i] == 'X')
			xsymb++;
	}
	if (cmp == 4)
		return 0;

	if (cmp + xsymb == 4)
		return xsymb;

	return -1;
}

const APDUCode *GetAPDUCode(uint8_t sw1, uint8_t sw2) {
	char buf[6] = {0};
	int mineq = APDUCodeTableLen;
	int mineqindx = 0;

	sprintf(buf, "%02X%02X", sw1, sw2);

	for (int i = 0; i < APDUCodeTableLen; i++) {
		int res = CodeCmp(APDUCodeTable[i].ID, buf);

		// equal
		if (res == 0) {
			return &APDUCodeTable[i];
		}

		// with some  'X'
		if (res > 0 && mineq > res) {
			mineq = res;
			mineqindx = i;
		}
	}

	// if we have not equal, but with some 'X'
	if (mineqindx < APDUCodeTableLen) {
		return &APDUCodeTable[mineqindx];
	}

	return NULL;
}

const char *GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2) {
	const APDUCode *cd = GetAPDUCode(sw1, sw2);
	if (cd)
		return cd->Description;
	else
		return APDUCodeTable[0].Description; //empty string
}

int APDUDecode(uint8_t *data, int len, APDUStruct *apdu) {
	ExtAPDUHeader *hapdu = (ExtAPDUHeader *)data;

	apdu->cla = hapdu->cla;
	apdu->ins = hapdu->ins;
	apdu->p1 = hapdu->p1;
	apdu->p2 = hapdu->p2;

	apdu->lc = 0;
	apdu->data = NULL;
	apdu->le = 0;
	apdu->extended_apdu = false;
	apdu->case_type = 0x00;

	uint8_t b0 = hapdu->lc[0];

	// case 1
	if (len == 4) {
		apdu->case_type = 0x01;
	}

	// case 2S (Le)
	if (len == 5) {
		apdu->case_type = 0x02;
		apdu->le = b0;
		if (!apdu->le)
			apdu->le = 0x100;
	}

	// case 3S (Lc + data)
	if (len == 5U + b0 && b0 != 0) {
		apdu->case_type = 0x03;
		apdu->lc = b0;
	}

	// case 4S (Lc + data + Le)
	if (len == 5U + b0 + 1U && b0 != 0) {
		apdu->case_type = 0x04;
		apdu->lc = b0;
		apdu->le = data[len - 1];
		if (!apdu->le)
			apdu->le = 0x100;
	}

	// extended length apdu
	if (len >= 7 && b0 == 0) {
		uint16_t extlen = (hapdu->lc[1] << 8) + hapdu->lc[2];

		// case 2E (Le) - extended
		if (len == 7) {
			apdu->case_type = 0x12;
			apdu->extended_apdu = true;
			apdu->le = extlen;
			if (!apdu->le)
				apdu->le = 0x10000;
		}

		// case 3E (Lc + data) - extended
		if (len == 7U + extlen) {
			apdu->case_type = 0x13;
			apdu->extended_apdu = true;
			apdu->lc = extlen;
		}

		// case 4E (Lc + data + Le) - extended 2-byte Le
		if (len == 7U + extlen + 2U) {
			apdu->case_type = 0x14;
			apdu->extended_apdu = true;
			apdu->lc = extlen;
			apdu->le = (data[len - 2] << 8) + data[len - 1];
			if (!apdu->le)
				apdu->le = 0x10000;
		}

		// case 4E (Lc + data + Le) - extended 3-byte Le
		if (len == 7U + extlen + 3U && data[len - 3] == 0) {
			apdu->case_type = 0x24;
			apdu->extended_apdu = true;
			apdu->lc = extlen;
			apdu->le = (data[len - 2] << 8) + data[len - 1];
			if (!apdu->le)
				apdu->le = 0x10000;
		}
	}

	if (!apdu->case_type)
		return 1;

	if (apdu->lc) {
		if (apdu->extended_apdu) {
			apdu->data = data + 7;
		} else {
			apdu->data = data + 5;
		}

	}

	return 0;
}

int APDUEncode(APDUStruct *apdu, uint8_t *data, int *len) {
	if (len)
		*len = 0;

	if (apdu->le > 0x10000 || apdu->lc > 0xffff)
		return 1;

	size_t dptr = 0;
	data[dptr++] = apdu->cla;
	data[dptr++] = apdu->ins;
	data[dptr++] = apdu->p1;
	data[dptr++] = apdu->p2;

	if (apdu->lc) {
		if (apdu->extended_apdu || apdu->lc > 0xff || apdu->le > 0x100) {
			data[dptr++] = 0x00;
			data[dptr++] = (apdu->lc >> 8) & 0xff;
			data[dptr++] = (apdu->lc) & 0xff;
			memmove(&data[dptr], apdu->data, apdu->lc);
			dptr += apdu->lc;
			apdu->extended_apdu = true;
		} else {
			data[dptr++] = apdu->lc;
			memmove(&data[dptr], apdu->data, apdu->lc);
			dptr += apdu->lc;
		}
	}

	if (apdu->le) {
		if (apdu->extended_apdu) {
			if (apdu->le != 0x10000) {
				data[dptr++] = 0x00;
				data[dptr++] = (apdu->le >> 8) & 0xff;
				data[dptr++] = (apdu->le) & 0xff;
			} else {
				data[dptr++] = 0x00;
				data[dptr++] = 0x00;
				data[dptr++] = 0x00;
			}
		} else {
			if (apdu->le != 0x100)
				data[dptr++] = apdu->le;
			else
				data[dptr++] = 0x00;
		}
	}

	if (len)
		*len = dptr;
	return 0;
}

int APDUEncodeS(sAPDU *sapdu, bool extended, uint16_t le, uint8_t *data, int *len) {
	if (extended && le > 0x100)
		return 10;

	APDUStruct apdu;

	apdu.cla = sapdu->CLA;
	apdu.ins = sapdu->INS;
	apdu.p1 = sapdu->P1;
	apdu.p2 = sapdu->P2;

	apdu.lc = sapdu->Lc;
	if (sapdu->Lc)
		apdu.data = sapdu->data;
	else
		apdu.data = NULL;
	apdu.le = le;

	apdu.extended_apdu = extended;
	apdu.case_type = 0x00;

	return APDUEncode(&apdu, data, len);
}

void APDUPrint(APDUStruct apdu) {
	APDUPrintEx(apdu, 0);
}

void APDUPrintEx(APDUStruct apdu, size_t maxdatalen) {
	PrintAndLogEx(INFO, "APDU: %scase=0x%02x cla=0x%02x ins=0x%02x p1=0x%02x p2=0x%02x Lc=0x%02x(%d) Le=0x%02x(%d)",
				  apdu.extended_apdu ? "[e]" : "", apdu.case_type, apdu.cla, apdu.ins, apdu.p1, apdu.p2, apdu.lc, apdu.lc, apdu.le, apdu.le);
	if (maxdatalen > 0)
		PrintAndLogEx(INFO, "data: %s%s", sprint_hex(apdu.data, MIN(apdu.lc, maxdatalen)), apdu.lc > maxdatalen ? "..." : "");
}
